package com.googlecode.lightest.core

import org.testng.TestNG
import org.testng.xml.XmlSuite

class TestRunner implements ITestRunner {
    GroovyClassLoader classLoader
    TestNG testng
    
    TestRunner() {
        classLoader = new GroovyClassLoader(this.class.classLoader)
        testng = new TestNG()
        testng.setUseDefaultListeners(false)
    }
    
    /**
     * Returns the output directory
     */
    File getOutputDir() {
        return new File(testng.getOutputDirectory())
    }
    
    /**
     * Configures the runner by wiring together dependencies as specified by
     * the configuration file.
     *
     * @param configFile
     */
    void configure(File configFile) {
        configure(configFile.text)
    }

    /**
     * Configures the runner by wiring together dependencies as specified by
     * the configuration text.
     *
     * @param configText
     */
    void configure(String configText) {
        IConfiguration config = new Configuration()
        config.init(configText)
        configure(config)
    }
    
    void configure(IConfiguration config = new Configuration()) {
        def classPaths = config.getClassPaths()
        def prefs = config.getPreferences()
        def envs = config.getEnvironments()
        def reporter = config.getReporter()
        def strategy = config.getDispatcherAssignmentStrategy()
        def dispatchers = []
        
        envs.each { env ->
            def dispatcher = new TaskDispatcher()
            dispatcher.setPreferences(prefs)
            dispatcher.setEnvironment(env)
            dispatcher.setStrategy(new SimpleTaskDispatchStrategy())
            dispatcher.setListener(reporter)
            
            dispatchers << dispatcher
        }
        
        strategy.setDispatchers(dispatchers)
        reporter.setDispatcherAssignmentStrategy(strategy)
        testng.addListener((Object) reporter)
        testng.setOutputDirectory(config.getOutputDir())
        
        classLoader = config.getClassLoader()
        
        classPaths.each { path ->
            def url = new File(path).getCanonicalFile().toURL()
            classLoader.addURL(url)
        }
    }
    
    /**
     * Sets the output directory
     *
     * @param outputDirectory
     */
    void setOutputDir(File dir) {
        testng.setOutputDirectory(dir.getCanonicalPath())
    }
    
    /**
     * Uses a given class loader to load a Groovy class from a file.
     * 
     * @param testFile
     * @param classLoader
     */
    private Class getClassFromFile(File testFile, GroovyClassLoader gcl) {
        assert testFile.exists()
        return classLoader.parseClass(testFile.text)
    }
    
    /**
     * Executes the test suites represented by XML files, along with tests
     * represented as Groovy files, whose paths are provided as a parameter to
     * this method. The contextual class loader of the TestNG run is a
     * GroovyClassLoader that is aware of any task classpaths specified in the
     * Configuration.
     *
     * @param paths  paths to files that are either Lightest suite XML files,
     *               or Groovy scripts that are LightestTestCase's.
     */
    void run(List<String> paths) {
        def suitePaths = []
        def testClasses = []
        
        paths.each { path ->
            if (path.endsWith('.xml')) {
                suitePaths << path
            }
            else if (path.endsWith('.groovy')) {
                testClasses << getClassFromFile(new File(path), classLoader)
            }
            else {
                println "Skipping unrecognized file: ${path}"
            }
        }
        
        if (testClasses) {
            suitePaths << LightestUtils.createSuiteFile(testClasses).getPath()
        }
        
        Thread.currentThread().setContextClassLoader(classLoader)
        
        testng.setTestSuites(suitePaths)
        testng.run()
    }
    
    static void main(args) {
        if (args.size() < 2) {
            println 'Usage: TestRunner CONFIGFILE [TESTFILE | SUITEFILE]...'
            System.exit(0)
        }
        
        def testRunner = new TestRunner()
        
        testRunner.configure(new File(args[0]))
        testRunner.run(args[1..args.size()-1])
    }
}
